/**
 * @module primitives
 * @description
 * 这个模块包含了所有用于从无到有创建基础几何体（“图元”）的函数。
 * 每个函数都会生成一个特定形状的 `TerrainSchematic` 实例，例如球体、长方体或圆锥体。
 */

import type TerrainSchematic from "../schematic";
import { SchematicBlock } from "../types";

/**
 * 生成一个球体。
 * @param schematic 要修改的蓝图实例。
 * @param radius 球体的半径。
 * @param voxelId 构成球体的方块ID。
 * @param voxelRot 构成球体的方块旋转。
 * @param options 球体选项，如是否空心。
 * @returns 返回传入的蓝图实例，以支持链式调用（尽管它通常用于初始化）。
 */
export function createSphere(
  schematic: TerrainSchematic,
  radius: number,
  voxelId: voxelId,
  voxelRot: voxelRotation,
  options: { isHollow?: boolean; thickness?: number } = {}
): TerrainSchematic {
  const { isHollow = false, thickness = 1 } = options;
  const center = radius;

  for (let x = 0; x <= 2 * radius; x++) {
    for (let y = 0; y <= 2 * radius; y++) {
      for (let z = 0; z <= 2 * radius; z++) {
        const dx = x - center;
        const dy = y - center;
        const dz = z - center;
        const distSq = dx * dx + dy * dy + dz * dz;

        if (distSq <= radius * radius) {
          if (
            !isHollow ||
            distSq >= (radius - thickness) * (radius - thickness)
          ) {
            schematic.schematicBlocks.push({ x, y, z, p: 0 });
          }
        }
      }
    }
  }

  // 设置调色板和边界
  schematic.data.palette = [voxelId, voxelRot];
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    new GameVector3(2 * radius, 2 * radius, 2 * radius)
  );

  return schematic;
}

/**
 * 生成一个圆柱体。
 * @param schematic 要修改的蓝图实例。
 * @param radius 圆柱的半径。
 * @param height 圆柱的高度。
 * @param voxelId 构成圆柱的方块ID。
 * @param voxelRot 构成圆柱的方块旋转。
 * @param options 圆柱选项，如是否空心。
 * @returns 返回传入的蓝图实例。
 */
export function createCylinder(
  schematic: TerrainSchematic,
  radius: number,
  height: number,
  voxelId: voxelId,
  voxelRot: voxelRotation,
  options: { isHollow?: boolean; thickness?: number } = {}
): TerrainSchematic {
  const { isHollow = false, thickness = 1 } = options;
  const center = radius;

  for (let y = 0; y < height; y++) {
    for (let x = 0; x <= 2 * radius; x++) {
      for (let z = 0; z <= 2 * radius; z++) {
        const dx = x - center;
        const dz = z - center;
        const distSq = dx * dx + dz * dz;

        if (distSq <= radius * radius) {
          if (
            !isHollow ||
            distSq >= (radius - thickness) * (radius - thickness)
          ) {
            schematic.schematicBlocks.push({ x, y, z, p: 0 });
          }
        }
      }
    }
  }

  schematic.data.palette = [voxelId, voxelRot];
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    new GameVector3(2 * radius, height - 1, 2 * radius)
  );

  return schematic;
}

/**
 * 生成一个金字塔。
 * @param schematic 要修改的蓝图实例。
 * @param baseSize 金字塔底座的边长。
 * @param height 金字塔的高度。
 * @param voxelId 构成金字塔的方块ID。
 * @param voxelRot 构成金字塔的方块旋转。
 * @param options 金字塔选项，如是否空心。
 * @returns 返回传入的蓝图实例。
 */
export function createPyramid(
  schematic: TerrainSchematic,
  baseSize: number,
  height: number,
  voxelId: voxelId,
  voxelRot: voxelRotation,
  options: { isHollow?: boolean } = {}
): TerrainSchematic {
  const { isHollow = false } = options;
  const halfBase = Math.floor(baseSize / 2);

  for (let y = 0; y < height; y++) {
    // 计算当前高度层的正方形边长的一半
    const layerHalfSize = Math.floor(halfBase * (1 - y / height));

    for (let x = -layerHalfSize; x <= layerHalfSize; x++) {
      for (let z = -layerHalfSize; z <= layerHalfSize; z++) {
        const blockX = x + halfBase;
        const blockZ = z + halfBase;

        if (isHollow) {
          // 如果是空心的，只在当前层的边缘放置方块
          if (Math.abs(x) === layerHalfSize || Math.abs(z) === layerHalfSize) {
            schematic.schematicBlocks.push({ x: blockX, y, z: blockZ, p: 0 });
          }
        } else {
          // 如果是实心的，填充整个正方形
          schematic.schematicBlocks.push({ x: blockX, y, z: blockZ, p: 0 });
        }
      }
    }
  }

  schematic.data.palette = [voxelId, voxelRot];
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    new GameVector3(baseSize - 1, height - 1, baseSize - 1)
  );

  return schematic;
}

/**
 * 生成一个长方体。
 * @param schematic 要修改的蓝图实例。
 * @param size 一个包含x,y,z尺寸的GameVector3对象。
 * @param voxelId 构成形状的方块ID。
 * @param voxelRot 构成形状的方块旋转。
 * @param options 其他选项，如是否空心。
 * @returns 返回传入的蓝图实例。
 */
export function createCuboid(
  schematic: TerrainSchematic,
  size: GameVector3,
  voxelId: voxelId,
  voxelRot: voxelRotation,
  options: { isHollow?: boolean; thickness?: number } = {}
): TerrainSchematic {
  const { isHollow = false, thickness = 1 } = options;

  for (let y = 0; y < size.y; y++) {
    for (let x = 0; x < size.x; x++) {
      for (let z = 0; z < size.z; z++) {
        if (
          !isHollow ||
          x < thickness ||
          x >= size.x - thickness ||
          y < thickness ||
          y >= size.y - thickness ||
          z < thickness ||
          z >= size.z - thickness
        ) {
          schematic.schematicBlocks.push({ x, y, z, p: 0 });
        }
      }
    }
  }

  schematic.data.palette = [voxelId, voxelRot];
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    size.sub(new GameVector3(1, 1, 1))
  );

  return schematic;
}

/**
 * 生成一个圆环体（甜甜圈形状）。
 * @param schematic 要修改的蓝图实例。
 * @param majorRadius 圆环的主半径（从圆心到管道中心的距离）。
 * @param minorRadius 圆环的次半径（管道的半径）。
 * @param voxelId 构成形状的方块ID。
 * @param voxelRot 构成形状的方块旋转。
 * @returns 返回传入的蓝图实例。
 */
export function createTorus(
  schematic: TerrainSchematic,
  majorRadius: number,
  minorRadius: number,
  voxelId: voxelId,
  voxelRot: voxelRotation
): TerrainSchematic {
  const size = (majorRadius + minorRadius) * 2;
  const center = size / 2;

  for (let y = 0; y < size; y++) {
    for (let x = 0; x < size; x++) {
      for (let z = 0; z < size; z++) {
        const dx = x - center;
        const dy = y - center;
        const dz = z - center;

        const dist_xz = Math.sqrt(dx * dx + dz * dz);
        if (dist_xz > 1e-6) {
          const torus_dist_sq =
            (majorRadius - dist_xz) * (majorRadius - dist_xz) + dy * dy;
          if (torus_dist_sq <= minorRadius * minorRadius) {
            schematic.schematicBlocks.push({ x, y, z, p: 0 });
          }
        }
      }
    }
  }

  schematic.data.palette = [voxelId, voxelRot];
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    new GameVector3(size - 1, size - 1, size - 1)
  );

  return schematic;
}

/**
 * 生成一个圆锥体。
 * @param schematic 要修改的蓝图实例。
 * @param radius 圆锥底部的半径。
 * @param height 圆锥的高度。
 * @param voxelId 构成形状的方块ID。
 * @param voxelRot 构成形状的方块旋转。
 * @param options 其他选项，如是否空心。
 * @returns 返回传入的蓝图实例。
 */
export function createCone(
  schematic: TerrainSchematic,
  radius: number,
  height: number,
  voxelId: voxelId,
  voxelRot: voxelRotation,
  options: { isHollow?: boolean; thickness?: number } = {}
): TerrainSchematic {
  const { isHollow = false, thickness = 1 } = options;
  const center = radius;

  for (let y = 0; y < height; y++) {
    const layerRadius = radius * (1 - y / height);
    for (let x = 0; x <= 2 * radius; x++) {
      for (let z = 0; z <= 2 * radius; z++) {
        const dx = x - center;
        const dz = z - center;
        const distSq = dx * dx + dz * dz;

        if (distSq <= layerRadius * layerRadius) {
          if (
            !isHollow ||
            distSq >= (layerRadius - thickness) * (layerRadius - thickness)
          ) {
            schematic.schematicBlocks.push({ x, y, z, p: 0 });
          }
        }
      }
    }
  }

  schematic.data.palette = [voxelId, voxelRot];
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    new GameVector3(2 * radius, height - 1, 2 * radius)
  );

  return schematic;
}

/**
 * 生成一个棱柱。
 * @param schematic 要修改的蓝图实例。
 * @param sides 棱柱底面的边数 (例如 3=三棱柱, 6=六棱柱)。
 * @param radius 棱柱底面外接圆的半径。
 * @param height 棱柱的高度。
 * @param voxelId 构成形状的方块ID。
 * @param voxelRot 构成形状的方块旋转。
 * @param options 其他选项，如是否空心。
 * @returns 返回传入的蓝图实例。
 */
export function createPrism(
  schematic: TerrainSchematic,
  sides: number,
  radius: number,
  height: number,
  voxelId: voxelId,
  voxelRot: voxelRotation,
  options: { isHollow?: boolean; thickness?: number } = {}
): TerrainSchematic {
  const { isHollow = false, thickness = 1 } = options;
  if (sides < 3) return schematic;

  const center = radius;
  const outerVertices: { x: number; z: number }[] = [];
  for (let i = 0; i < sides; i++) {
    const angle = (2 * Math.PI * i) / sides;
    outerVertices.push({
      x: center + radius * Math.cos(angle),
      z: center + radius * Math.sin(angle),
    });
  }

  const innerRadius = radius - thickness;
  const innerVertices: { x: number; z: number }[] = [];
  if (isHollow && innerRadius > 0) {
    for (let i = 0; i < sides; i++) {
      const angle = (2 * Math.PI * i) / sides;
      innerVertices.push({
        x: center + innerRadius * Math.cos(angle),
        z: center + innerRadius * Math.sin(angle),
      });
    }
  }

  for (let y = 0; y < height; y++) {
    for (
      let x = Math.floor(center - radius);
      x <= Math.ceil(center + radius);
      x++
    ) {
      for (
        let z = Math.floor(center - radius);
        z <= Math.ceil(center + radius);
        z++
      ) {
        if (isPointInPolygon(x, z, outerVertices)) {
          if (!isHollow) {
            schematic.schematicBlocks.push({ x, y, z, p: 0 });
          } else {
            if (innerRadius <= 0 || !isPointInPolygon(x, z, innerVertices)) {
              schematic.schematicBlocks.push({ x, y, z, p: 0 });
            }
          }
        }
      }
    }
  }

  schematic.data.palette = [voxelId, voxelRot];
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    new GameVector3(Math.ceil(2 * radius), height - 1, Math.ceil(2 * radius))
  );

  return schematic;
}

function isPointInPolygon(
  x: number,
  z: number,
  vertices: { x: number; z: number }[]
): boolean {
  let isInside = false;
  for (let i = 0, j = vertices.length - 1; i < vertices.length; j = i++) {
    const xi = vertices[i].x,
      zi = vertices[i].z;
    const xj = vertices[j].x,
      zj = vertices[j].z;

    const intersect =
      zi > z !== zj > z && x < ((xj - xi) * (z - zi)) / (zj - zi) + xi;
    if (intersect) isInside = !isInside;
  }
  return isInside;
}

/**
 * 生成一个管道（空心圆柱体）。
 * @param schematic 要修改的蓝图实例。
 * @param height 管道的高度。
 * @param radius 管道的外半径。
 * @param thickness 管道的壁厚。
 * @param voxelId 构成形状的方块ID。
 * @param voxelRot 构成形状的方块旋转。
 * @returns 返回传入的蓝图实例。
 */
export function createPipe(
  schematic: TerrainSchematic,
  height: number,
  radius: number,
  thickness: number,
  voxelId: voxelId,
  voxelRot: voxelRotation
): TerrainSchematic {
  const innerRadius = radius - thickness;
  const radiusSq = radius * radius;
  const innerRadiusSq = innerRadius * innerRadius;
  const center = radius;

  for (let y = 0; y < height; y++) {
    for (let x = 0; x <= 2 * radius; x++) {
      for (let z = 0; z <= 2 * radius; z++) {
        const dx = x - center;
        const dz = z - center;
        const distSq = dx * dx + dz * dz;

        if (distSq <= radiusSq && distSq > innerRadiusSq) {
          schematic.schematicBlocks.push({ x, y, z, p: 0 });
        }
      }
    }
  }

  schematic.data.palette = [voxelId, voxelRot];
  const sizeX = 2 * radius + 1;
  const sizeZ = 2 * radius + 1;
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    new GameVector3(sizeX, height, sizeZ)
  );

  return schematic;
}

/**
 * 生成一个胶囊体。
 * @param schematic 要修改的蓝图实例。
 * @param height 胶囊体中间圆柱部分的高度。
 * @param radius 胶囊体的半径。
 * @param voxelId 构成形状的方块ID。
 * @param voxelRot 构成形状的方块旋转。
 * @returns 返回传入的蓝图实例。
 */
export function createCapsule(
  schematic: TerrainSchematic,
  height: number,
  radius: number,
  voxelId: voxelId,
  voxelRot: voxelRotation
): TerrainSchematic {
  const radiusSq = radius * radius;
  const center = radius;

  // Cylinder part
  for (let y = radius; y < radius + height; y++) {
    for (let x = 0; x <= 2 * radius; x++) {
      for (let z = 0; z <= 2 * radius; z++) {
        const dx = x - center;
        const dz = z - center;
        if (dx * dx + dz * dz <= radiusSq) {
          schematic.schematicBlocks.push({ x, y, z, p: 0 });
        }
      }
    }
  }

  // Hemispheres part
  for (let y = 0; y < 2 * radius; y++) {
    for (let x = 0; x <= 2 * radius; x++) {
      for (let z = 0; z <= 2 * radius; z++) {
        const dx = x - center;
        const dy = y - radius; // Center of sphere is at y=radius
        const dz = z - center;
        if (dx * dx + dy * dy + dz * dz <= radiusSq) {
          if (y < radius) {
            // Bottom hemisphere
            schematic.schematicBlocks.push({ x, y, z, p: 0 });
          } else {
            // Top hemisphere, shifted by cylinder height
            schematic.schematicBlocks.push({ x, y: y + height, z, p: 0 });
          }
        }
      }
    }
  }

  schematic.data.palette = [voxelId, voxelRot];
  const totalHeight = height + 2 * radius;
  const sizeX = 2 * radius + 1;
  const sizeZ = 2 * radius + 1;
  schematic.data.bounds = new GameBounds3(
    new GameVector3(0, 0, 0),
    new GameVector3(sizeX, totalHeight, sizeZ)
  );

  return schematic;
}
